`include "../codigo/Comp.v"
`include "../codigo/Controle.v"

module Decode (

    input        [0:0]  clock,
    input        [0:0]  reset,
    input        [0:0]  fw_if_id_stall,

    // Fetch
    input       [31:0]  if_id_instrucao,
    input       [31:0]  if_id_proximopc,
    output reg   [0:0]  id_if_selfontepc,
    output      [31:0]  id_if_rega,
    output reg  [31:0]  id_if_pcimd2ext,
    output reg  [31:0]  id_if_pcindex,
    output reg   [1:0]  id_if_seltipopc,

    // Execute
    output reg   [0:0]  id_ex_selalushift,
    output reg   [0:0]  id_ex_selimregb,
    output reg   [0:0]  id_ex_selsarega,
    output reg   [2:0]  id_ex_aluop,
    output reg   [0:0]  id_ex_unsig,
    output reg   [1:0]  id_ex_shiftop,
    output reg   [4:0]  id_ex_shiftamt,
    output      [31:0]  id_ex_rega,
    output reg   [2:0]  id_ex_msm,
    output reg   [2:0]  id_ex_msl,
    output reg   [0:0]  id_ex_readmem,
    output reg   [0:0]  id_ex_writemem,
    output reg   [0:0]  id_ex_mshw,
    output reg   [0:0]  id_ex_lshw,
    output      [31:0]  id_ex_regb,
    output reg  [31:0]  id_ex_imedext,
    output reg  [31:0]  id_ex_proximopc,
    output reg   [2:0]  id_ex_selwsource,
    output reg   [4:0]  id_ex_regdest,
    output reg   [0:0]  id_ex_writereg,
    output reg   [0:0]  id_ex_writeov,

    // Forwarding 
    output       [4:0]  id_fw_regdest,
    output       [0:0]  id_fw_load,
    output       [4:0]  id_fw_addra,
    output       [4:0]  id_fw_addrb,
    output      [31:0]  id_fw_rega,
    output      [31:0]  id_fw_regb,
    input       [31:0]  fw_id_rega,
    input       [31:0]  fw_id_regb,
    

    // Registradores
    output       [4:0]  id_reg_addra,
    output       [4:0]  id_reg_addrb,
    input       [31:0]  reg_id_dataa,
    input       [31:0]  reg_id_datab,
    output       [0:0]  id_reg_ena,
    output       [0:0]  id_reg_enb

    );

    // Control port wires
    wire   [5:0]    op;
    wire   [5:0]    fn;
    wire   [4:0]    rt;
    wire   [2:0]    selwsource;
    wire   [1:0]    selregdest;
    wire   [0:0]    writereg;
    wire   [0:0]    writeov;
    wire   [0:0]    selimregb;
    wire   [0:0]    selsarega;
    wire   [0:0]    selalushift;
    wire   [2:0]    aluop;
    wire   [0:0]    unsig;
    wire   [1:0]    shiftop;
    wire   [0:0]    mshw;
    wire   [0:0]    lshw;
    wire   [2:0]    msm;
    wire   [2:0]    msl;
    wire   [0:0]    readmem;
    wire   [0:0]    writemem;
    wire   [1:0]    selbrjumpz;
    wire   [1:0]    seltipopc;
    wire   [2:0]    compop;

    // Controller
    Controle CONTROLE(
		.op(op),
		.fn(fn),
		.rt(rt),
		.selwsource(selwsource),
		.selregdest(selregdest),
		.writereg(writereg),
		.writeov(writeov),
		.selimregb(selimregb),
		.selsarega(selsarega),
		.selalushift(selalushift),
		.aluop(aluop),
		.unsig(unsig),
		.shiftop(shiftop),
		.mshw(mshw),
		.lshw(lshw),
		.msm(msm),
		.msl(msl),
		.readmem(readmem),
		.writemem(writemem),
		.selbrjumpz(selbrjumpz),
		.seltipopc(seltipopc),
		.compop(compop)
	);


    // Comparator
    wire compout; 
    Comp COMP(
		.a(comp_rega),
		.b(comp_regb),
		.op(compop),
		.compout(compout)
	);

    
    // Shifter
    wire [31:0] w_im_extend;
    wire [31:0] w_im_extend_shift;
	assign w_im_extend_shift = w_im_extend << 2'b10;
    
    wire [31:0] w_index_extend;
    wire [31:0] w_index_extend_shift;
	assign w_index_extend_shift = w_index_extend << 2'b10 ;


    // Auxiliary registers
    reg [31:0] comp_rega;
    reg [31:0] comp_regb;
    reg [31:0] im_extend;
    reg [31:0] im_extend_shift;
    reg [31:0] index_extend;
    reg [31:0] index_extend_shift;
    reg [4:0]  reg_id_reg_addrb;
    reg [0:0]  reg_id_reg_ena;
    reg [0:0]  reg_id_reg_enb;
    reg [5:0]  op_aux;
    reg [0:0]  reg_id_fw_load;
    reg [4:0]  reg_id_fw_addrb;
    reg [4:0]  temp_regdest;


    // Assignments
    // Control instruction parts
    assign op = if_id_instrucao[31:26];
    assign fn = if_id_instrucao[5:0];
    assign rt = if_id_instrucao[20:16];
     
    // Register A
    assign id_reg_addra = if_id_instrucao[25:21]; 
    
    // Sign extended
    assign w_im_extend = im_extend;
    assign w_index_extend = index_extend;
    
    // Outputs A and B
    assign id_ex_rega = fw_id_rega;
    assign id_ex_regb = fw_id_regb;  
    
    // Register signs and address
    assign id_reg_addrb = reg_id_reg_addrb;
    assign id_reg_ena = reg_id_reg_ena;
    assign id_reg_enb = reg_id_reg_enb;

    // Forward values
    assign id_fw_load = reg_id_fw_load;
    assign id_fw_addra = if_id_instrucao[25:21];
    assign id_fw_addrb = reg_id_fw_addrb;
    assign id_fw_rega = reg_id_dataa;
    assign id_fw_regb = reg_id_datab;
    assign id_fw_regdest = temp_regdest;

    // Forwards the address in reg A to fetch
    assign id_if_rega = reg_id_dataa;

    // Reset signs
    always @(posedge(reset)) begin

        id_ex_selalushift = 1'b0;
        id_ex_selimregb = 1'b0;
        id_ex_selsarega = 1'b0;
        id_ex_aluop = 3'b000;
        id_ex_unsig = 1'b0;     
        id_ex_shiftop = 2'b00;
        id_ex_shiftamt = 5'b00000;
        id_ex_msm = 3'b000;
        id_ex_msl = 3'b000; 
        id_ex_readmem = 1'b0; 
        id_ex_writemem = 1'b0;
        id_ex_mshw = 1'b0;     
        id_ex_lshw = 1'b0;     
        id_ex_imedext = 32'b00000000000000000000000000000000;
        id_ex_proximopc = 32'b00000000000000000000000000000000;
        id_ex_selwsource = 3'b000;
        id_ex_regdest = 5'b00000;
        id_ex_writereg = 1'b0;
        id_ex_writeov = 1'b0;  

    end


    always @(posedge(clock) or negedge(clock)) begin

        // ------------------------------------------------------
        // Fetch

        // Sign extension to shifter
        im_extend[15:0] = if_id_instrucao[15:0];	        
        if (im_extend[15] == 1'b1) begin
            im_extend[31:16] = 16'b1111111111111111;
        end else begin
            im_extend[31:16] = 16'b0000000000000000;
        end
        im_extend_shift = w_im_extend_shift; 
          
        // PC from ID to IF
        id_if_pcimd2ext = (im_extend_shift + if_id_proximopc);
        id_if_seltipopc = seltipopc; 

        // Index extension to shifter
        index_extend[25:0] = if_id_instrucao[25:0];
        index_extend[31:26] = 6'b000000;
        index_extend_shift = w_index_extend_shift;
 
        // Sign id_if_pcindex
        id_if_pcindex[31:28] = if_id_proximopc[31:28];
        id_if_pcindex[27:0] = index_extend_shift[27:0];


        // ------------------------------------------------------
        // Registers
        reg_id_reg_ena = 1'b1;
        reg_id_reg_enb = 1'b1;
        op_aux = if_id_instrucao[31:26];
       
        if (op_aux == 6'd1 || op_aux == 6'd6 || op_aux == 6'd7) begin
            reg_id_reg_addrb = 5'b00000;
        end else begin
            reg_id_reg_addrb = if_id_instrucao[20:16];
        end 
	

        // ------------------------------------------------------
        // Forward
        reg_id_fw_load = readmem;
        reg_id_fw_addrb = if_id_instrucao[20:16];
        

        // ------------------------------------------------------
        // Execute
        case(selregdest)

            2'b00: 
            begin
                temp_regdest = if_id_instrucao[20:16];    
            end

            2'b01:
            begin
                temp_regdest = if_id_instrucao[15:11];
            end

            2'b10:
            begin
                temp_regdest = 5'd31;
            end

            2'b11: 
            begin
                temp_regdest = 5'dx;
            end

        endcase 


        // ------------------------------------------------------
        // Stall 
        if(!fw_if_id_stall) begin

            id_ex_proximopc = if_id_proximopc;
            id_ex_selalushift = selalushift;
            id_ex_selimregb = selimregb;
            id_ex_selsarega = selsarega;
            id_ex_aluop = aluop;
            id_ex_unsig = unsig;     
            id_ex_shiftop = shiftop;
            id_ex_shiftamt = if_id_instrucao[10:6];
            id_ex_msm = msm;
            id_ex_msl = msl; 
            id_ex_readmem = readmem; 
            id_ex_writemem = writemem;
            id_ex_mshw = mshw;     
            id_ex_lshw = lshw;     
            id_ex_imedext[15:0] = if_id_instrucao[15:0];
        
            if(id_ex_imedext[15] == 1'b1) begin
                id_ex_imedext[31:16] = 16'b1111111111111111;
            end else begin
                id_ex_imedext[31:16] = 16'b0000000000000000;
            end
        
            id_ex_selwsource = selwsource;
            id_ex_writereg = writereg;
            id_ex_writeov = writeov;             
            id_ex_regdest = temp_regdest;
        
        end else begin   

            id_ex_selalushift = 1'b0;
            id_ex_selimregb = 1'b0;
            id_ex_selsarega = 1'b0;
            id_ex_aluop = 3'b000;
            id_ex_unsig = 1'b0;     
            id_ex_shiftop = 2'b00;
            id_ex_shiftamt = 5'b00000;
            id_ex_msm = 3'b000;
            id_ex_msl = 3'b000; 
            id_ex_readmem = 1'b0; 
            id_ex_writemem = 1'b0;
            id_ex_mshw = 1'b0;     
            id_ex_lshw = 1'b0;     
            id_ex_imedext = 32'b00000000000000000000000000000000;
            id_ex_proximopc = 32'b00000000000000000000000000000000;
            id_ex_selwsource = 3'b000;
            id_ex_regdest = 5'b00000;
            id_ex_writereg = 1'b0;
            id_ex_writeov = 1'b0; 

        end 

        // ------------------------------------------------------
        // Comparator data 
        comp_rega = reg_id_dataa;
        comp_regb = reg_id_datab;

        // ------------------------------------------------------
        // Resolves PC source
        case (selbrjumpz)

            2'b00: begin
                id_if_selfontepc = 1'b0;
            end

            2'b01: begin
                id_if_selfontepc = 1'b1;
            end
    
            2'b10: begin
                id_if_selfontepc = compout;
            end

            2'b11: begin
                id_if_selfontepc = 1'b0;
            end

        endcase

    end 

endmodule
